<?php

declare(strict_types=1);

namespace EthanZ\HyperfExt\Exception\Handler;

use EthanZ\HyperfExt\Common\TraceEntity;
use EthanZ\HyperfExt\Constants\Code;
use EthanZ\HyperfExt\Exception\BaseException;
use Hyperf\Logger\LoggerFactory;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Hyperf\Validation\ValidationException;
use Throwable;
use Psr\Log\LoggerInterface;

/**
 * 处理所有Http服务异常.
 *
 * Class HttpExceptionHandler
 */
class HttpExceptionHandler extends ExceptionHandler
{


    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @var BaseException
     */
    protected $response;

    /**
     * @var Code
     */
    protected $baseCode;


    public function __construct(BaseException $response, Code $baseCode, LoggerFactory $loggerFactory)
    {
        $this->response = $response;
        $this->baseCode = $baseCode;
        $this->logger   = $loggerFactory->get('log', 'default');
    }


    /**
     * @param Throwable         $throwable
     * @param ResponseInterface $response
     *
     * @return ResponseInterface
     */
    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 阻止异常冒泡
        $this->stopPropagation();

        $code    = $throwable->getCode();
        $message = $throwable->getMessage();
        $file    = $throwable->getFile();
        $line    = $throwable->getLine();

        // 未找到数据.
//        if (get_class($throwable) == 'Hyperf\Database\Model\ModelNotFoundException') {
//            $code    = Code::NOT_FOUND_DATA;
//            $message = Code::getMessage(Code::NOT_FOUND_DATA);
//        }

        // 判断是否为验证抛错.
        $needLog = 1;
        if ($throwable instanceof ValidationException) {
            $code    = Code::ERROR[0];
            $message = implode(';', $throwable->validator->errors()->all());
            $needLog = 0;
        } elseif ($throwable instanceof BaseException) {
            $needLog   = $throwable->needLog();
            $throwable = $throwable->exception();
        }

        // 格式化输出.
        $result = [
            'code'    => $code,
            'message' => $message,
            'data'    => null,
        ];
        if (env('APP_ENV') !== 'prod') {
            $result['exception'] = [
                'file'    => $file,
                'line'    => $line,
                'message' => $message,
            ];
            // 获取链路信息.
            $result['debugData'] = TraceEntity::getInstance()->getDebugData();
        }

        if ($needLog) {
            // 记录日志.
            $this->logger->error($message, $result);
        }

        $data = json_encode(
            $result,
            JSON_UNESCAPED_UNICODE
        );

        return $response->withStatus(200)->withBody(new SwooleStream($data));
    }


    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}
